#include "Node.h"

#include <QDebug>

#include <math.h>
#include <GammaNode.h>

Node::Node(double sampleValue) :
    QObject(),
    lastSample(sampleValue),
    nodeType(CONSTANT),
    isObserved(false),
    proposalSigma(1)
{
    sampleHistory.append(lastSample);
}

Node::Node(NodeType type, double sampleValue, bool isObserved, QString label) :
    QObject(),
    lastSample(sampleValue),
    nodeType(type),
    isObserved(isObserved),
    proposalSigma(1),
    nodeLabel(label)
{
    sampleHistory.append(lastSample);
}

Node::Node(const Node &other) :
    QObject(),
    lastSample(other.lastSample),
    sampleHistory(other.sampleHistory),
    nodeChildren(other.nodeChildren),
    nodeType(other.nodeType),
    isObserved(other.isObserved),
    proposalSigma(other.proposalSigma),
    nodeLabel(other.nodeLabel)
{
}

Node::~Node()
{

}

double Node::logLikelihood()
{
    return this->logLikelihood(lastSample);
}

void Node::setProposalStandardDeviation(double proposalStdDev)
{
    proposalSigma = proposalStdDev;
}

QVector<Node*> Node::children()
{
    return nodeChildren;
}

void Node::addChild(Node* node)
{
    nodeChildren.append(node);
}

double Node::getLastSample()
{
    return lastSample;
}

double Node::sample()
{
    if (!this->shouldSample()) {

        sampleHistory.append(lastSample);
        return lastSample;
    }

    // Sample from symtmeric proposal distribution
    double xStar = Distribution::sharedInstance()->randomNormal(lastSample, proposalSigma);

    if (nodeType == POISSON) {
        xStar = (int) round(xStar);
    } else if (nodeType == BETA) {
        if (xStar <= 0.0 || xStar >= 1.0) {
            sampleHistory.append(lastSample);
            return lastSample;
        }
    } else if (nodeType == GAMMA || nodeType == INVERSE_GAMMA) {
        if (xStar <= 0.0) {
            sampleHistory.append(lastSample);
            return lastSample;
        }
    }

    // Compute importance ratio
    double fOfXStar = this->logLikelihood(xStar);
    double fOfLastSample = this->logLikelihood();
    double oldsampleValue = lastSample;
    lastSample = xStar;
    for (int i = 0; i < nodeChildren.count(); i++) {
        fOfXStar += nodeChildren[i]->logLikelihood();
    }
    lastSample = oldsampleValue;
    for (int i = 0; i < nodeChildren.count(); i++) {
        fOfLastSample += nodeChildren[i]->logLikelihood();
    }

    double importanceRatio = fOfXStar - fOfLastSample;

    double randomNumber = log(Distribution::sharedInstance()->randomUniform(0, 1));

    if (randomNumber < importanceRatio) {
        lastSample = xStar;
    }

    sampleHistory.append(lastSample);

    return lastSample;
}

bool Node::shouldSample()
{
    return nodeType != CONSTANT && nodeType != MISC && !isObserved;
}

void Node::setSampleHistory(QVector<double> history)
{
    sampleHistory = history;
}

QVector<double> Node::getSampleHistory()
{
    return sampleHistory;
}

void Node::dropSampleHistoryUpTo(int number)
{
    sampleHistory.remove(0, number);
}

double Node::mean()
{
    return Distribution::sharedInstance()->mean(sampleHistory);
}

double Node::variance()
{
    return Distribution::sharedInstance()->variance(sampleHistory);
}

double Node::minimum()
{
    return Distribution::sharedInstance()->minimum(sampleHistory);
}

double Node::maximum()
{
    return Distribution::sharedInstance()->maximum(sampleHistory);
}

QVector<int> Node::histogram()
{
    return Distribution::sharedInstance()->histogram(sampleHistory, 100);
}

QVector<QPointF> Node::histogramLine()
{
    return Distribution::sharedInstance()->histogramLine(sampleHistory, 100);
}

QString Node::label()
{
    if (nodeLabel.isEmpty()) {
        size_t pointer = reinterpret_cast<size_t>(this);
        nodeLabel = QString::number(pointer);
    }
    return nodeLabel;
}

